# SEE modeldata package for new datasets
library(tidyverse)         # for graphing and data cleaning
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2     ✓ purrr   0.3.4
## ✓ tibble  3.1.0     ✓ dplyr   1.0.5
## ✓ tidyr   1.1.2     ✓ stringr 1.4.0
## ✓ readr   1.3.1     ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(tidymodels)        # for modeling
## ── Attaching packages ────────────────────────────────────── tidymodels 0.1.2 ──
## ✓ broom     0.7.3      ✓ recipes   0.1.15
## ✓ dials     0.0.9      ✓ rsample   0.0.9 
## ✓ infer     0.5.4      ✓ tune      0.1.3 
## ✓ modeldata 0.1.0      ✓ workflows 0.2.2 
## ✓ parsnip   0.1.5      ✓ yardstick 0.0.7
## ── Conflicts ───────────────────────────────────────── tidymodels_conflicts() ──
## x scales::discard() masks purrr::discard()
## x dplyr::filter()   masks stats::filter()
## x recipes::fixed()  masks stringr::fixed()
## x dplyr::lag()      masks stats::lag()
## x yardstick::spec() masks readr::spec()
## x recipes::step()   masks stats::step()
library(stacks)            # for stacking models
library(naniar)            # for examining missing values (NAs)
library(lubridate)         # for date manipulation
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(moderndive)        # for King County housing data
library(vip)               # for variable importance plots
## 
## Attaching package: 'vip'
## The following object is masked from 'package:utils':
## 
##     vi
library(DALEX)             # for model interpretation  
## Welcome to DALEX (version: 2.2.0).
## Find examples and detailed introduction at: http://ema.drwhy.ai/
## 
## Attaching package: 'DALEX'
## The following object is masked from 'package:dplyr':
## 
##     explain
library(DALEXtra)          # for extension of DALEX
## Anaconda not found on your computer. Conda related functionality such as create_env.R and condaenv and yml parameters from explain_scikitlearn will not be available
library(patchwork)         # for combining plots nicely
library(kknn)              # for knn models
theme_set(theme_minimal()) # Lisa's favorite theme
data("lending_club")
# Data dictionary (as close as I could find): https://www.kaggle.com/wordsforthewise/lending-club/discussion/170691

When you finish the assignment, remove the # from the options chunk at the top, so that messages and warnings aren’t printed. If you are getting errors in your code, add error = TRUE so that the file knits. I would recommend not removing the # until you are completely finished.

Put it on GitHub!

From now on, GitHub should be part of your routine when doing assignments. I recommend making it part of your process anytime you are working in R, but I’ll make you show it’s part of your process for assignments.

Task: When you are finished with the assignment, post a link below to the GitHub repo for the assignment. If you want to post it to your personal website, that’s ok (not required). Make sure the link goes to a spot in the repo where I can easily find this assignment. For example, if you have a website with a blog and post the assignment as a blog post, link to the post’s folder in the repo. As an example, I’ve linked to my GitHub stacking material here.

Link to Assignment 2

Modeling

Before jumping into these problems, you should read through (and follow along with!) the model stacking and global model interpretation tutorials on the Course Materials tab of the course website.

We’ll be using the lending_club dataset from the modeldata library, which is part of tidymodels. The data dictionary they reference doesn’t seem to exist anymore, but it seems the one on this kaggle discussion is pretty close. It might also help to read a bit about Lending Club before starting in on the exercises.

The outcome we are interested in predicting is Class. And according to the dataset’s help page, its values are “either ‘good’ (meaning that the loan was fully paid back or currently on-time) or ‘bad’ (charged off, defaulted, of 21-120 days late)”.

Tasks: I will be expanding these, but this gives a good outline.

  1. Explore the data, concentrating on examining distributions of variables and examining missing values.

Distributions of quantitative variables

lending_club %>% 
  select(where(is.numeric)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_histogram() +
  facet_wrap(vars(variable), 
             scales = "free")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

#cleaning: remove delinq amount dataset b/c it was highly correlated probably

Categorical variables

lending_club %>% 
  select(where(is.factor)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_bar() +
  facet_wrap(vars(variable), 
             scales = "free", 
             nrow = 2)

Looking at missing values

gg_miss_var(lending_club)

#No missing values!
  1. Do any data cleaning steps that need to happen before the model is build. For example, you might remove any variables that mean the same thing as the response variable (not sure if that happens here), get rid of rows where all variables have missing values, etc.
#remove variables that have overwhelming amounts of zeros and are likely highly correlated with the outcome
lending_club <- lending_club %>%
  select(-delinq_amnt,-acc_now_delinq)

#lump states based on region
regions <- read.csv("statesregions.csv")

regions <- regions %>%
  mutate(addr_state = State.Code)

#changing the addr_state variable to Region so it has less levels
lending_club <- lending_club %>%
  left_join(regions) %>%
  select(-addr_state, -State, -State.Code, -Division) %>%
  mutate(Region = as.factor(Region))
## Joining, by = "addr_state"
lending_club

Be sure to add more “bad” Classes. This is not the best solution, but it will work for now. (Should investigate how to appropriately use step_sample_up() function from themis).

set.seed(494)

create_more_bad <- lending_club %>% 
  filter(Class == "bad") %>% 
  sample_n(size = 3000, replace = TRUE)

lending_club_mod <- lending_club %>% 
  bind_rows(create_more_bad)
  1. Split the data into training and test, putting 75% in the training data.
set.seed(494) # for reproducibility

#randomly assign 75% of the data to training
lending_club_split <- initial_split(lending_club_mod, strata = 'Class', prop = .75)

lending_club_split
## <Analysis/Assess/Total>
## <9643/3214/12857>
#training and testing datasets
lending_club_training <- training(lending_club_split)
lending_club_testing <- testing(lending_club_split)
  1. Set up the recipe and the pre-processing steps to build a lasso model. Some steps you should take:
  • Make all integer variables numeric (I’d highly recommend using step_mutate_at() or this will be a lot of code). We’ll want to do this for the model interpretation we’ll do later.
  • Think about grouping factor variables with many levels. I did it for the states above because it felt more intuitive for me to modify the dataset before I made the mod so I could get rid of addr_state.
#seeing which factor variables have many levels
lapply(lending_club, levels)
## $funded_amnt
## NULL
## 
## $term
## [1] "term_36" "term_60"
## 
## $int_rate
## NULL
## 
## $sub_grade
##  [1] "A1" "A2" "A3" "A4" "A5" "B1" "B2" "B3" "B4" "B5" "C1" "C2" "C3" "C4" "C5"
## [16] "D1" "D2" "D3" "D4" "D5" "E1" "E2" "E3" "E4" "E5" "F1" "F2" "F3" "F4" "F5"
## [31] "G1" "G2" "G3" "G4" "G5"
## 
## $verification_status
## [1] "Not_Verified"    "Source_Verified" "Verified"       
## 
## $annual_inc
## NULL
## 
## $emp_length
##  [1] "emp_lt_1"  "emp_1"     "emp_ge_10" "emp_2"     "emp_3"     "emp_4"    
##  [7] "emp_5"     "emp_6"     "emp_7"     "emp_8"     "emp_9"     "emp_unk"  
## 
## $delinq_2yrs
## NULL
## 
## $inq_last_6mths
## NULL
## 
## $revol_util
## NULL
## 
## $open_il_6m
## NULL
## 
## $open_il_12m
## NULL
## 
## $open_il_24m
## NULL
## 
## $total_bal_il
## NULL
## 
## $all_util
## NULL
## 
## $inq_fi
## NULL
## 
## $inq_last_12m
## NULL
## 
## $num_il_tl
## NULL
## 
## $total_il_high_credit_limit
## NULL
## 
## $Class
## [1] "bad"  "good"
## 
## $Region
## [1] "Midwest"   "Northeast" "South"     "West"
#lump all the As together, and Bs, etc.
  • Make categorical variables dummy variables (make sure NOT to do this to the outcome variable).
  • Normalize quantitative variables.
lending_recipe <- recipe(Class ~ ., data = lending_club_training) %>%
  step_mutate_at(all_numeric(), fn = ~ as.numeric(.)) %>% #make all numeric vars numeric
  step_mutate(sub_grade = fct_collapse(sub_grade, A = c("A1","A2","A3","A4","A5"), 
               B = c("B1","B2","B3","B4","B5"), 
               C = c("C1","C2","C3","C4","C5"), #collapsing levels in sub_grade into less
               D = c("D1","D2","D3","D4","D5"), 
               E = c("E1","E2","E3","E4","E5"),
               f = c("F1","F2","F3","F4","F5"),
               G = c("G1","G2","G3","G4","G5"))) %>%
  step_normalize(all_predictors(),-all_nominal()) %>% #normalize quantitative vars
  step_dummy(all_nominal(),-all_outcomes()) #make categorical vars dummy vars


#using prep and juice to make sure everything went as planned
lending_recipe %>%
  prep(lending_club_training) %>%
  juice()
  1. Set up the lasso model and workflow. We will tune the penalty parameter.
#lasso model
lending_lasso_mod <-
  logistic_reg(mixture = 1) %>%
  set_engine("glmnet") %>%
  set_args(penalty = tune()) %>%
  set_mode("classification")

#lasso workflow
lending_lasso_wf <-
  workflow() %>%
  add_recipe(lending_recipe) %>%
  add_model(lending_lasso_mod)

lending_lasso_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: logistic_reg()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 4 Recipe Steps
## 
## ● step_mutate_at()
## ● step_mutate()
## ● step_normalize()
## ● step_dummy()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Logistic Regression Model Specification (classification)
## 
## Main Arguments:
##   penalty = tune()
##   mixture = 1
## 
## Computational engine: glmnet
  1. Set up the model tuning for the penalty parameter. Be sure to add the control_stack_grid() for the control argument so we can use these results later when we stack. Find the accuracy and area under the roc curve for the model with the best tuning parameter. Use 5-fold cv.
set.seed(494) #for reproducible 5-fold

#5-fold cross-validation
lending_cv <- vfold_cv(lending_club_training, v = 5)

#penalty grid: 10 levels
penalty_grid <- grid_regular(penalty(),
                             levels = 10)

#make sure predictions and workflows are saved for stacking in the future
ctrl_grid <- control_stack_grid()

#tune using 5-fold cv
lending_lasso_tune <-
  lending_lasso_wf %>%
  tune_grid(
    resamples = lending_cv,
    grid = penalty_grid,
    control = ctrl_grid
  )

lending_accuracy <- lending_lasso_tune %>%
  collect_metrics() %>%
  filter(.metric == "accuracy")

lending_roc_auc <- lending_lasso_tune %>%
  collect_metrics() %>%
  filter(.metric == "roc_auc")

ggplot(lending_accuracy, aes(x = penalty, y = mean)) +
  geom_point() +
  geom_line() +
  scale_x_log10(
   breaks = scales::trans_breaks("log10", function(x) 10^x),
   labels = scales::trans_format("log10",scales::math_format(10^.x))) +
  ggtitle("Accuracy over different penalties") +
  ylab("accuracy")

ggplot(lending_roc_auc, aes(x = penalty, y = mean)) +
  geom_point() +
  geom_line() +
  scale_x_log10(
   breaks = scales::trans_breaks("log10", function(x) 10^x),
   labels = scales::trans_format("log10",scales::math_format(10^.x))) +
  ggtitle("Area under roc curve over different penalties") +
  ylab("roc_auc")

#select best tuning parameter (accuracy and area under roc curve)
best_param_lending <- lending_lasso_tune %>%
  select_best(metric = "accuracy", "roc_auc")

#Is it always the case that the best tuning parameter will be the same for these two metrics?
best_param_lending
#best tuning parameter is 0.0004641589

#accuracy for best tuning parameter: 0.7487296
#area under roc curve for best tuning parameter: 0.7656651
# finalize workflow
lending_lasso_final_wf <- lending_lasso_wf %>%
  finalize_workflow(best_param_lending)

#fit final model with best tuning parameter
lending_lasso_final_mod <- lending_lasso_final_wf %>%
  fit(data = lending_club)
  1. Set up the recipe and the pre-processing steps to build a random forest model. You shouldn’t have to do as many steps. The only step you should need to do is making all integers numeric.
#preprocessing
lending_recipe_forest <- recipe(Class ~ ., data = lending_club_training) %>%
  step_mutate_at(all_numeric(), fn = ~ as.numeric(.))
  1. Set up the random forest model and workflow. We will tune the mtry and min_n parameters and set the number of trees, trees, to 100 (otherwise the next steps take too long).
#build model
lending_forest_mod <-
  rand_forest(mtry = tune(), min_n = tune(), trees = 200) %>%
  set_mode("classification") %>%
  set_engine("ranger")

#workflow
lending_forest_wf <-
  workflow() %>%
  add_recipe(lending_recipe_forest) %>%
  add_model(lending_forest_mod)

lending_forest_wf
## ══ Workflow ════════════════════════════════════════════════════════════════════
## Preprocessor: Recipe
## Model: rand_forest()
## 
## ── Preprocessor ────────────────────────────────────────────────────────────────
## 1 Recipe Step
## 
## ● step_mutate_at()
## 
## ── Model ───────────────────────────────────────────────────────────────────────
## Random Forest Model Specification (classification)
## 
## Main Arguments:
##   mtry = tune()
##   trees = 200
##   min_n = tune()
## 
## Computational engine: ranger
  1. Set up the model tuning for both the mtry and min_n parameters. Be sure to add the control_stack_grid() for the control argument so we can use these results later when we stack. Use only 3 levels in the grid. For the mtry parameter, you need to put finalize(mtry(), lending_training %>% select(-Class)) in as an argument instead of just mtry(), where lending_training is the name of your training data. This is because the mtry() grid will otherwise have unknowns in it. This part can take a while to run.
set.seed(494)

metric <- metric_set(roc_auc, accuracy)

#choose penalty parameter values
penalty_grid_forest <- grid_regular(finalize(mtry(), lending_club_training %>% select(-Class)), min_n(), levels = 3)

penalty_grid_forest
ctrl_grid <- control_stack_grid()

#tune using 5-fold cv
lending_forest_tune <-
  lending_forest_wf %>%
  tune_grid(
    resamples = lending_cv,
    grid = penalty_grid_forest,
    control = ctrl_grid,
    metrics = metric
  )

lending_forest_tune
  1. Find the best tuning parameters. What are the accuracy and area under the ROC curve for the model with those tuning parameters?
#select best tuning parameter (accuracy-wise)
best_accuracy_lending <- lending_forest_tune %>%
  select_best(metric = "accuracy")

best_accuracy_lending
best_roc_lending <- lending_forest_tune %>%
  select_best(metric = "roc_auc")

best_roc_lending
lending_forest_accuracy <- lending_forest_tune %>%
  collect_metrics() %>%
  filter(.metric == "accuracy")

lending_forest_roc <- lending_forest_tune %>%
  collect_metrics() %>%
  filter(.metric == "roc_auc")

#best mtry is 10 and best min_n is 2

#accuracy for best tuning parameters: 0.9905639

#roc_auc for best tuning parameters: 0.9949828
# finalize workflow
lending_forest_final_wf <- lending_forest_wf %>%
  finalize_workflow(best_accuracy_lending)

#fit final model with best tuning parameter
lending_forest_final_mod <- lending_forest_final_wf %>%
  fit(data = lending_club_training)
  1. Use functions from the DALEX and DALEXtra libraries to create a histogram and boxplot of the residuals from the training data. How do they look? Any interesting behavior?
#create explainer ("an object that provides a uniform interface for different models.")
lending_forest_explain <- 
  explain_tidymodels(
    model = lending_forest_final_mod,
    data = lending_club_training %>% 
      select(-Class), 
    y = lending_club_training %>%
      mutate(Class_num = as.integer(Class =="good")) %>%
      pull(Class_num),
    label = "rf"
  )
## Preparation of a new explainer is initiated
##   -> model label       :  rf 
##   -> data              :  9643  rows  20  cols 
##   -> data              :  tibble converted into a data.frame 
##   -> target variable   :  9643  values 
##   -> predict function  :  yhat.workflow  will be used (  default  )
##   -> predicted values  :  No value for predict function target column. (  default  )
##   -> model_info        :  package tidymodels , ver. 0.1.2 , task classification (  default  ) 
##   -> predicted values  :  numerical, min =  0 , mean =  0.7031608 , max =  1  
##   -> residual function :  difference between y and yhat (  default  )
##   -> residuals         :  numerical, min =  -0.41 , mean =  0.02327284 , max =  0.2525  
##   A new explainer has been created! 
#create explainer ("an object that provides a uniform interface for different models.")
lending_lasso_explain <- 
  explain_tidymodels(
    model = lending_lasso_final_mod,
    data = lending_club_training %>%
      select(-Class), 
    y = lending_club_training %>%
      mutate(Class_num = as.integer(Class =="good")) %>%
      pull(Class_num),
    label = "lasso"
  )
## Preparation of a new explainer is initiated
##   -> model label       :  lasso 
##   -> data              :  9643  rows  20  cols 
##   -> data              :  tibble converted into a data.frame 
##   -> target variable   :  9643  values 
##   -> predict function  :  yhat.workflow  will be used (  default  )
##   -> predicted values  :  No value for predict function target column. (  default  )
##   -> model_info        :  package tidymodels , ver. 0.1.2 , task classification (  default  ) 
##   -> predicted values  :  numerical, min =  0.4558564 , mean =  0.9353142 , max =  0.9966348  
##   -> residual function :  difference between y and yhat (  default  )
##   -> residuals         :  numerical, min =  -0.993638 , mean =  -0.2088805 , max =  0.4951364  
##   A new explainer has been created! 
lasso_mod_perf <- model_performance(lending_lasso_explain)
rf_mod_perf <- model_performance(lending_forest_explain)

#histogram of residuals of training data
hist_plot <- 
  plot(rf_mod_perf, 
       geom = "histogram")

box_plot <-
  plot(rf_mod_perf, 
       geom = "boxplot")

hist_plot + box_plot

Although this matters more for quantitative variables want the plot of the residuals to be normally distributed. In this case, the residuals seem to have a giant spike at 0 (which is good) and are slightly right-skewed.

  1. Use DALEX functions to create a variable importance plot from this model. What are the most important variables?
set.seed(10) #since we are sampling & permuting, we set a seed so we can replicate the results
rf_var_imp <- 
  model_parts(
    lending_forest_explain
    )

plot(rf_var_imp, show_boxplots = TRUE)

#Here, the most important variables in predicting Class are annual_inc (self-reported annual income), int_rate (interest rate on the loan), and revol_util (amount of credit borrower is using relative to all available revolving credit)
  1. Write a function called cp_profile to make a CP profile. The function will take an explainer, a new observation, and a variable name as its arguments and create a CP profile for a quantitative predictor variable. You will need to use the predict_profile() function inside the function you create - put the variable name there so the plotting part is easier. You’ll also want to use aes_string() rather than aes() and quote the variables. Use the cp_profile() function to create one CP profile of your choosing. Be sure to choose a variable that is numeric, not integer. There seem to be issues with those that I’m looking into.
#ceteris parabus profiles, or CP profiles, show how one variable affects the outcome, holding all other variables fixed, for one observation.

#var_name must be numeric (not integer) and in quotations
cp_profile <- function(explainer, new_obs, var_name) {
  cpp <- predict_profile(explainer = explainer, 
                  new_observation = new_obs,
                  variables = var_name)
  cpp %>% rename(yhat = `_yhat_`) %>% 
   ggplot(aes_string(x = var_name,
              y = "yhat")) +
   geom_line() 
}

obs4 <- lending_club_training %>% 
  slice(4)
obs4
cp_profile(lending_forest_explain, obs4, "int_rate")

For an extra challenge, write a function that will work for either a quantitative or categorical variable.

If you need help with function writing check out the Functions chapter of R4DS by Wickham and Grolemund.

  1. Use DALEX functions to create partial dependence plots (with the CP profiles in gray) for the 3-4 most important variables. If the important variables are categorical, you can instead make a CP profile for 3 observations in the dataset and discuss how you could go about constructing a partial dependence plot for a categorical variable (you don’t have to code it, but you can if you want an extra challenge). If it ever gives you an error that says, “Error: Can’t convert from VARIABLE to VARIABLE due to loss of precision”, then remove that variable from the list. I seem to have figured out why it’s doing that, but I don’t know how to fix it yet.
set.seed(494) # since we take a sample of 100 obs
# This takes a while to run. 
# If we only want to examine a few variables, add variables argument to model_profile.

rf_pdp <- model_profile(explainer = lending_forest_explain, variables = c("int_rate", "annual_inc", "revol_util"))

plot(rf_pdp, 
     variables = c("int_rate", "annual_inc", "revol_util"),
     geom = "profiles")

  1. Fit one more model type of your choosing that will feed into the stacking model.

K-nearest neighbors

# create a model definition
lending_knn_mod <-
  nearest_neighbor(
    neighbors = tune("k")
  ) %>%
  set_engine("kknn") %>% 
  set_mode("classification")

# create the workflow
knn_wf <- 
  workflow() %>% 
  add_model(lending_knn_mod) %>%
  add_recipe(lending_recipe)

# tune it using 4 tuning parameters
lending_knn_tune <- 
  knn_wf %>% 
  tune_grid(
    lending_cv,
    metrics = metric,
    grid = 4,
    control = ctrl_grid
  )
  1. Create a model stack with the candidate models from the previous parts of the exercise and use the blend_predictions() function to find the coefficients of the stacked model. Create a plot examining the performance metrics for the different penalty parameters to assure you have captured the best one. If not, adjust the penalty. (HINT: use the autoplot() function). Which models are contributing most?
lending_stack <-
  stacks() %>%
  add_candidates(lending_forest_tune) %>%
  add_candidates(lending_lasso_tune) %>%
  add_candidates(lending_knn_tune)
## Warning: Predictions from the candidates c(".pred_bad_lending_lasso_tune_1_02",
## ".pred_bad_lending_lasso_tune_1_03", ".pred_bad_lending_lasso_tune_1_04",
## ".pred_bad_lending_lasso_tune_1_05", ".pred_bad_lending_lasso_tune_1_06",
## ".pred_good_lending_lasso_tune_1_02", ".pred_good_lending_lasso_tune_1_03",
## ".pred_good_lending_lasso_tune_1_04", ".pred_good_lending_lasso_tune_1_05",
## ".pred_good_lending_lasso_tune_1_06") were identical to those from existing
## candidates and were removed from the data stack.
as_tibble(lending_stack)
lending_blend <- 
  lending_stack %>% 
  blend_predictions()

lending_blend
## ── A stacked ensemble model ─────────────────────────────────────
## 
## Out of 18 possible candidate members, the ensemble retained 1.
## Lasso penalty: 0.1.
## 
## The 1 highest weighted member classes are:
## # A tibble: 1 x 3
##   member                             type        weight
##   <chr>                              <chr>        <dbl>
## 1 .pred_good_lending_forest_tune_1_2 rand_forest   4.90
## 
## Members have not yet been fitted with `fit_members()`.
autoplot(lending_blend)

The random forest model is contributing the most (in fact, it’s the only one contributing in this case?).

  1. Fit the final stacked model using fit_members(). Apply the model to the test data and report the accuracy and area under the curve. Create a graph of the ROC and construct a confusion matrix. Comment on what you see. Save this final model using the saveRDS() function - see the Use the model section of the tidymodels intro. We are going to use the model in the next part. You’ll want to save it in the folder where you create your shiny app.
lending_final_stack <- lending_blend %>% 
  fit_members()

lending_final_stack
## ── A stacked ensemble model ─────────────────────────────────────
## 
## Out of 18 possible candidate members, the ensemble retained 1.
## Lasso penalty: 0.1.
## 
## The 1 highest weighted member classes are:
## # A tibble: 1 x 3
##   member                             type        weight
##   <chr>                              <chr>        <dbl>
## 1 .pred_good_lending_forest_tune_1_2 rand_forest   4.90
final_stack_table <- lending_final_stack %>% 
  predict(new_data = lending_club_testing) %>% 
  bind_cols(lending_club_testing) %>%
  mutate(correct_prediction = ifelse(.pred_class == Class, TRUE, FALSE))

#accuracy
final_stack_accuracy <- accuracy(final_stack_table, Class, .pred_class)
final_stack_accuracy
#accuracy is 0.9965775

#area under the curve
lending_final_stack$metrics %>% 
  filter(.metric == "roc_auc")
#roc_auc is 0.9949827

#ROC
autoplot(lending_final_stack)

#confusion matrix
conf_mat(data = final_stack_table, truth = Class, estimate = .pred_class)
##           Truth
## Prediction  bad good
##       bad   868    0
##       good   11 2335

**I see that this blended model (which is essentially the random forest model with the best tuning parameters) has a super high accuracy and roc_auc.

Shiny app

If you are new to Shiny apps or it’s been awhile since you’ve made one, visit the Shiny links on our course Resource page. I would recommend starting with my resource because it will be the most basic. You won’t be doing anything super fancy in this app.

Everyone should watch the Theming Shiny talk by Carson Sievert so you can make your app look amazing.

Tasks:

You are going to create an app that allows a user to explore how the predicted probability of a loan being paid back (or maybe just the predicted class - either “good” or “bad”) changes depending on the values of the predictor variables.

Specifically, you will do the following:

  • Set up a separate project and GitHub repo for this app. Make sure the saved model from the previous problem is also in that folder. The app needs to be created in a file called exactly app.R that is also in the project folder.
  • At the top of the file, load any libraries you use in the app.
  • Use the readRDS() function to load the model.
  • You may want to load some of the data to use
  • Create a user interface (using the various *Input() functions) where someone could enter values for each variable that feeds into the model. You will want to think hard about which types of *Input() functions to use. Think about how you can best prevent mistakes (eg. entering free text could lead to many mistakes).
  • Another part of the user interface will allow them to choose a variable (you can limit this to only the quantitative variables) where they can explore the effects of changing that variable, holding all others constant.
  • After the user has entered all the required values, the output will be a CP profile with the the predicted value for the data that was entered, indicated by a point. I don’t think the functions from DALEX and DALEXtra will work with a stacked model, so you’ll likely have to (get to) do some of your own coding.
  • Use the bslib to theme your shiny app!
  • Publish your app to shinyapps.io. There are instructions for doing that on the tutorial I linked to above.
  • Write a paragraph or two describing your app on your website! Link to the app and your GitHub repository in your post. Include a link to your post here.

Coded Bias

Watch the Code Bias film and write a short reflection. If you want some prompts, reflect on: What part of the film impacted you the most? Was there a part that surprised you and why? What emotions did you experience while watching?

I think that the quote and common theme that “the past dwells within our algorithms” is crucial to be aware of. How the data was collected and who is represented (and who isn’t) reflects where our society was at the time of collection. Therefore, how are we going to make progress in being less discriminatory and more inclusive in the future if the algorithms are basing their assignments on the past? I wonder if there is some way to simulate future data or just do something to make past data more representative of current and future needs.

Coded Bias also led me to think a lot about how we compare our society with one such as China. We feel relieved that we don’t live in a society with constant facial recognition and social credit scores, but our society is tracking us and being exclusionary based on the (often inaccurate) data they gather about us. We have pervasive algorithmic bias that is geared toward capitalistic gain, keeping the rich rich, and keeping minority populations down (ex. the facial recognition in the apartment building from the documentary). It was creepy (and true) that the documentary said that the main difference between the surveillance of the US and China is that China is being transparent about it, whereas we are letting it fly under the radar.

REMEMBER TO ADD YOUR GITHUB LINK AT THE TOP OF THE PAGE AND UNCOMMENT THE knitr OPTIONS.

LS0tCnRpdGxlOiAnQXNzaWdubWVudCAjMicKYXV0aG9yOiAnQ29sbGVlbiBNaW5uaWhhbicKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoja25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFKQpgYGAKCmBgYHtyIGxpYnJhcmllc30KIyBTRUUgbW9kZWxkYXRhIHBhY2thZ2UgZm9yIG5ldyBkYXRhc2V0cwpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICAgICAjIGZvciBncmFwaGluZyBhbmQgZGF0YSBjbGVhbmluZwpsaWJyYXJ5KHRpZHltb2RlbHMpICAgICAgICAjIGZvciBtb2RlbGluZwpsaWJyYXJ5KHN0YWNrcykgICAgICAgICAgICAjIGZvciBzdGFja2luZyBtb2RlbHMKbGlicmFyeShuYW5pYXIpICAgICAgICAgICAgIyBmb3IgZXhhbWluaW5nIG1pc3NpbmcgdmFsdWVzIChOQXMpCmxpYnJhcnkobHVicmlkYXRlKSAgICAgICAgICMgZm9yIGRhdGUgbWFuaXB1bGF0aW9uCmxpYnJhcnkobW9kZXJuZGl2ZSkgICAgICAgICMgZm9yIEtpbmcgQ291bnR5IGhvdXNpbmcgZGF0YQpsaWJyYXJ5KHZpcCkgICAgICAgICAgICAgICAjIGZvciB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3RzCmxpYnJhcnkoREFMRVgpICAgICAgICAgICAgICMgZm9yIG1vZGVsIGludGVycHJldGF0aW9uICAKbGlicmFyeShEQUxFWHRyYSkgICAgICAgICAgIyBmb3IgZXh0ZW5zaW9uIG9mIERBTEVYCmxpYnJhcnkocGF0Y2h3b3JrKSAgICAgICAgICMgZm9yIGNvbWJpbmluZyBwbG90cyBuaWNlbHkKbGlicmFyeShra25uKSAgICAgICAgICAgICAgIyBmb3Iga25uIG1vZGVscwp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKSAjIExpc2EncyBmYXZvcml0ZSB0aGVtZQpgYGAKCmBgYHtyIGRhdGF9CmRhdGEoImxlbmRpbmdfY2x1YiIpCiMgRGF0YSBkaWN0aW9uYXJ5IChhcyBjbG9zZSBhcyBJIGNvdWxkIGZpbmQpOiBodHRwczovL3d3dy5rYWdnbGUuY29tL3dvcmRzZm9ydGhld2lzZS9sZW5kaW5nLWNsdWIvZGlzY3Vzc2lvbi8xNzA2OTEKYGBgCgoKV2hlbiB5b3UgZmluaXNoIHRoZSBhc3NpZ25tZW50LCByZW1vdmUgdGhlIGAjYCBmcm9tIHRoZSBvcHRpb25zIGNodW5rIGF0IHRoZSB0b3AsIHNvIHRoYXQgbWVzc2FnZXMgYW5kIHdhcm5pbmdzIGFyZW4ndCBwcmludGVkLiBJZiB5b3UgYXJlIGdldHRpbmcgZXJyb3JzIGluIHlvdXIgY29kZSwgYWRkIGBlcnJvciA9IFRSVUVgIHNvIHRoYXQgdGhlIGZpbGUga25pdHMuIEkgd291bGQgcmVjb21tZW5kIG5vdCByZW1vdmluZyB0aGUgYCNgIHVudGlsIHlvdSBhcmUgY29tcGxldGVseSBmaW5pc2hlZC4KCiMjIFB1dCBpdCBvbiBHaXRIdWIhICAgICAgICAKCkZyb20gbm93IG9uLCBHaXRIdWIgc2hvdWxkIGJlIHBhcnQgb2YgeW91ciByb3V0aW5lIHdoZW4gZG9pbmcgYXNzaWdubWVudHMuIEkgcmVjb21tZW5kIG1ha2luZyBpdCBwYXJ0IG9mIHlvdXIgcHJvY2VzcyBhbnl0aW1lIHlvdSBhcmUgd29ya2luZyBpbiBSLCBidXQgSSdsbCBtYWtlIHlvdSBzaG93IGl0J3MgcGFydCBvZiB5b3VyIHByb2Nlc3MgZm9yIGFzc2lnbm1lbnRzLgoKKipUYXNrKio6IFdoZW4geW91IGFyZSBmaW5pc2hlZCB3aXRoIHRoZSBhc3NpZ25tZW50LCBwb3N0IGEgbGluayBiZWxvdyB0byB0aGUgR2l0SHViIHJlcG8gZm9yIHRoZSBhc3NpZ25tZW50LiBJZiB5b3Ugd2FudCB0byBwb3N0IGl0IHRvIHlvdXIgcGVyc29uYWwgd2Vic2l0ZSwgdGhhdCdzIG9rIChub3QgcmVxdWlyZWQpLiBNYWtlIHN1cmUgdGhlIGxpbmsgZ29lcyB0byBhIHNwb3QgaW4gdGhlIHJlcG8gd2hlcmUgSSBjYW4gZWFzaWx5IGZpbmQgdGhpcyBhc3NpZ25tZW50LiBGb3IgZXhhbXBsZSwgaWYgeW91IGhhdmUgYSB3ZWJzaXRlIHdpdGggYSBibG9nIGFuZCBwb3N0IHRoZSBhc3NpZ25tZW50IGFzIGEgYmxvZyBwb3N0LCBsaW5rIHRvIHRoZSBwb3N0J3MgZm9sZGVyIGluIHRoZSByZXBvLiBBcyBhbiBleGFtcGxlLCBJJ3ZlIGxpbmtlZCB0byBteSBHaXRIdWIgc3RhY2tpbmcgbWF0ZXJpYWwgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9sbGVuZHdheS9hZHNfd2Vic2l0ZS90cmVlL21hc3Rlci9fcG9zdHMvMjAyMS0wMy0yMi1zdGFja2luZykuCgpbTGluayB0byBBc3NpZ25tZW50IDJdKGh0dHBzOi8vZ2l0aHViLmNvbS9uZWVsbG9jMjgvQWR2YW5jZWREYXRhU2NpLWh3MikKCiMjIE1vZGVsaW5nCgpCZWZvcmUganVtcGluZyBpbnRvIHRoZXNlIHByb2JsZW1zLCB5b3Ugc2hvdWxkIHJlYWQgdGhyb3VnaCAoYW5kIGZvbGxvdyBhbG9uZyB3aXRoISkgdGhlIFttb2RlbCBzdGFja2luZ10oaHR0cHM6Ly9hZHZhbmNlZC1kcy1pbi1yLm5ldGxpZnkuYXBwL3Bvc3RzLzIwMjEtMDMtMjItc3RhY2tpbmcvKSBhbmQgW2dsb2JhbCBtb2RlbCBpbnRlcnByZXRhdGlvbl0oaHR0cHM6Ly9hZHZhbmNlZC1kcy1pbi1yLm5ldGxpZnkuYXBwL3Bvc3RzLzIwMjEtMDMtMjQtaW1sZ2xvYmFsLykgdHV0b3JpYWxzIG9uIHRoZSBDb3Vyc2UgTWF0ZXJpYWxzIHRhYiBvZiB0aGUgY291cnNlIHdlYnNpdGUuCgpXZSdsbCBiZSB1c2luZyB0aGUgYGxlbmRpbmdfY2x1YmAgZGF0YXNldCBmcm9tIHRoZSBgbW9kZWxkYXRhYCBsaWJyYXJ5LCB3aGljaCBpcyBwYXJ0IG9mIGB0aWR5bW9kZWxzYC4gVGhlIGRhdGEgZGljdGlvbmFyeSB0aGV5IHJlZmVyZW5jZSBkb2Vzbid0IHNlZW0gdG8gZXhpc3QgYW55bW9yZSwgYnV0IGl0IHNlZW1zIHRoZSBvbmUgb24gdGhpcyBba2FnZ2xlIGRpc2N1c3Npb25dKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vd29yZHNmb3J0aGV3aXNlL2xlbmRpbmctY2x1Yi9kaXNjdXNzaW9uLzE3MDY5MSkgaXMgcHJldHR5IGNsb3NlLiBJdCBtaWdodCBhbHNvIGhlbHAgdG8gcmVhZCBhIGJpdCBhYm91dCBbTGVuZGluZyBDbHViXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MZW5kaW5nQ2x1YikgYmVmb3JlIHN0YXJ0aW5nIGluIG9uIHRoZSBleGVyY2lzZXMuCgpUaGUgb3V0Y29tZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBwcmVkaWN0aW5nIGlzIGBDbGFzc2AuIEFuZCBhY2NvcmRpbmcgdG8gdGhlIGRhdGFzZXQncyBoZWxwIHBhZ2UsIGl0cyB2YWx1ZXMgYXJlICJlaXRoZXIgJ2dvb2QnIChtZWFuaW5nIHRoYXQgdGhlIGxvYW4gd2FzIGZ1bGx5IHBhaWQgYmFjayBvciBjdXJyZW50bHkgb24tdGltZSkgb3IgJ2JhZCcgKGNoYXJnZWQgb2ZmLCBkZWZhdWx0ZWQsIG9mIDIxLTEyMCBkYXlzIGxhdGUpIi4KCioqVGFza3M6KiogSSB3aWxsIGJlIGV4cGFuZGluZyB0aGVzZSwgYnV0IHRoaXMgZ2l2ZXMgYSBnb29kIG91dGxpbmUuCgoxLiBFeHBsb3JlIHRoZSBkYXRhLCBjb25jZW50cmF0aW5nIG9uIGV4YW1pbmluZyBkaXN0cmlidXRpb25zIG9mIHZhcmlhYmxlcyBhbmQgZXhhbWluaW5nIG1pc3NpbmcgdmFsdWVzLgoKRGlzdHJpYnV0aW9ucyBvZiBxdWFudGl0YXRpdmUgdmFyaWFibGVzCmBgYHtyfQpsZW5kaW5nX2NsdWIgJT4lIAogIHNlbGVjdCh3aGVyZShpcy5udW1lcmljKSkgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKSArCiAgZmFjZXRfd3JhcCh2YXJzKHZhcmlhYmxlKSwgCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZSIpCgojY2xlYW5pbmc6IHJlbW92ZSBkZWxpbnEgYW1vdW50IGRhdGFzZXQgYi9jIGl0IHdhcyBoaWdobHkgY29ycmVsYXRlZCBwcm9iYWJseQpgYGAKCkNhdGVnb3JpY2FsIHZhcmlhYmxlcwpgYGB7cn0KbGVuZGluZ19jbHViICU+JSAKICBzZWxlY3Qod2hlcmUoaXMuZmFjdG9yKSkgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsIAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUpKSArCiAgZ2VvbV9iYXIoKSArCiAgZmFjZXRfd3JhcCh2YXJzKHZhcmlhYmxlKSwgCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZSIsIAogICAgICAgICAgICAgbnJvdyA9IDIpCmBgYAoKTG9va2luZyBhdCBtaXNzaW5nIHZhbHVlcwpgYGB7cn0KZ2dfbWlzc192YXIobGVuZGluZ19jbHViKQoKI05vIG1pc3NpbmcgdmFsdWVzIQpgYGAKCgoKCjIuIERvIGFueSBkYXRhIGNsZWFuaW5nIHN0ZXBzIHRoYXQgbmVlZCB0byBoYXBwZW4gYmVmb3JlIHRoZSBtb2RlbCBpcyBidWlsZC4gRm9yIGV4YW1wbGUsIHlvdSBtaWdodCByZW1vdmUgYW55IHZhcmlhYmxlcyB0aGF0IG1lYW4gdGhlIHNhbWUgdGhpbmcgYXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIChub3Qgc3VyZSBpZiB0aGF0IGhhcHBlbnMgaGVyZSksIGdldCByaWQgb2Ygcm93cyB3aGVyZSBhbGwgdmFyaWFibGVzIGhhdmUgbWlzc2luZyB2YWx1ZXMsIGV0Yy4KYGBge3J9CiNyZW1vdmUgdmFyaWFibGVzIHRoYXQgaGF2ZSBvdmVyd2hlbG1pbmcgYW1vdW50cyBvZiB6ZXJvcyBhbmQgYXJlIGxpa2VseSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIHRoZSBvdXRjb21lCmxlbmRpbmdfY2x1YiA8LSBsZW5kaW5nX2NsdWIgJT4lCiAgc2VsZWN0KC1kZWxpbnFfYW1udCwtYWNjX25vd19kZWxpbnEpCgojbHVtcCBzdGF0ZXMgYmFzZWQgb24gcmVnaW9uCnJlZ2lvbnMgPC0gcmVhZC5jc3YoInN0YXRlc3JlZ2lvbnMuY3N2IikKCnJlZ2lvbnMgPC0gcmVnaW9ucyAlPiUKICBtdXRhdGUoYWRkcl9zdGF0ZSA9IFN0YXRlLkNvZGUpCgojY2hhbmdpbmcgdGhlIGFkZHJfc3RhdGUgdmFyaWFibGUgdG8gUmVnaW9uIHNvIGl0IGhhcyBsZXNzIGxldmVscwpsZW5kaW5nX2NsdWIgPC0gbGVuZGluZ19jbHViICU+JQogIGxlZnRfam9pbihyZWdpb25zKSAlPiUKICBzZWxlY3QoLWFkZHJfc3RhdGUsIC1TdGF0ZSwgLVN0YXRlLkNvZGUsIC1EaXZpc2lvbikgJT4lCiAgbXV0YXRlKFJlZ2lvbiA9IGFzLmZhY3RvcihSZWdpb24pKQoKbGVuZGluZ19jbHViCmBgYAoKCkJlIHN1cmUgdG8gYWRkIG1vcmUgImJhZCIgQ2xhc3Nlcy4gVGhpcyBpcyBub3QgdGhlIGJlc3Qgc29sdXRpb24sIGJ1dCBpdCB3aWxsIHdvcmsgZm9yIG5vdy4gKFNob3VsZCBpbnZlc3RpZ2F0ZSBob3cgdG8gYXBwcm9wcmlhdGVseSB1c2UgYHN0ZXBfc2FtcGxlX3VwKClgIGZ1bmN0aW9uIGZyb20gW2B0aGVtaXNgXShodHRwczovL2dpdGh1Yi5jb20vdGlkeW1vZGVscy90aGVtaXMpKS4KCmBgYHtyfQpzZXQuc2VlZCg0OTQpCgpjcmVhdGVfbW9yZV9iYWQgPC0gbGVuZGluZ19jbHViICU+JSAKICBmaWx0ZXIoQ2xhc3MgPT0gImJhZCIpICU+JSAKICBzYW1wbGVfbihzaXplID0gMzAwMCwgcmVwbGFjZSA9IFRSVUUpCgpsZW5kaW5nX2NsdWJfbW9kIDwtIGxlbmRpbmdfY2x1YiAlPiUgCiAgYmluZF9yb3dzKGNyZWF0ZV9tb3JlX2JhZCkKYGBgCgozLiBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0LCBwdXR0aW5nIDc1XCUgaW4gdGhlIHRyYWluaW5nIGRhdGEuCgpgYGB7cn0Kc2V0LnNlZWQoNDk0KSAjIGZvciByZXByb2R1Y2liaWxpdHkKCiNyYW5kb21seSBhc3NpZ24gNzUlIG9mIHRoZSBkYXRhIHRvIHRyYWluaW5nCmxlbmRpbmdfY2x1Yl9zcGxpdCA8LSBpbml0aWFsX3NwbGl0KGxlbmRpbmdfY2x1Yl9tb2QsIHN0cmF0YSA9ICdDbGFzcycsIHByb3AgPSAuNzUpCgpsZW5kaW5nX2NsdWJfc3BsaXQKCiN0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhc2V0cwpsZW5kaW5nX2NsdWJfdHJhaW5pbmcgPC0gdHJhaW5pbmcobGVuZGluZ19jbHViX3NwbGl0KQpsZW5kaW5nX2NsdWJfdGVzdGluZyA8LSB0ZXN0aW5nKGxlbmRpbmdfY2x1Yl9zcGxpdCkKYGBgCgo0LiBTZXQgdXAgdGhlIHJlY2lwZSBhbmQgdGhlIHByZS1wcm9jZXNzaW5nIHN0ZXBzIHRvIGJ1aWxkIGEgbGFzc28gbW9kZWwuIFNvbWUgc3RlcHMgeW91IHNob3VsZCB0YWtlOgoKKiBNYWtlIGFsbCBpbnRlZ2VyIHZhcmlhYmxlcyBudW1lcmljIChJJ2QgaGlnaGx5IHJlY29tbWVuZCB1c2luZyBgc3RlcF9tdXRhdGVfYXQoKWAgb3IgdGhpcyB3aWxsIGJlIGEgbG90IG9mIGNvZGUpLiBXZSdsbCB3YW50IHRvIGRvIHRoaXMgZm9yIHRoZSBtb2RlbCBpbnRlcnByZXRhdGlvbiB3ZSdsbCBkbyBsYXRlci4gIAoqIFRoaW5rIGFib3V0IGdyb3VwaW5nIGZhY3RvciB2YXJpYWJsZXMgd2l0aCBtYW55IGxldmVscy4gKipJIGRpZCBpdCBmb3IgdGhlIHN0YXRlcyBhYm92ZSBiZWNhdXNlIGl0IGZlbHQgbW9yZSBpbnR1aXRpdmUgZm9yIG1lIHRvIG1vZGlmeSB0aGUgZGF0YXNldCBiZWZvcmUgSSBtYWRlIHRoZSBtb2Qgc28gSSBjb3VsZCBnZXQgcmlkIG9mIGFkZHJfc3RhdGUuKioKYGBge3J9IAojc2VlaW5nIHdoaWNoIGZhY3RvciB2YXJpYWJsZXMgaGF2ZSBtYW55IGxldmVscwpsYXBwbHkobGVuZGluZ19jbHViLCBsZXZlbHMpCiNsdW1wIGFsbCB0aGUgQXMgdG9nZXRoZXIsIGFuZCBCcywgZXRjLgpgYGAKCiogTWFrZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgZHVtbXkgdmFyaWFibGVzIChtYWtlIHN1cmUgTk9UIHRvIGRvIHRoaXMgdG8gdGhlIG91dGNvbWUgdmFyaWFibGUpLiAgCiogTm9ybWFsaXplIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuICAKCmBgYHtyfQpsZW5kaW5nX3JlY2lwZSA8LSByZWNpcGUoQ2xhc3MgfiAuLCBkYXRhID0gbGVuZGluZ19jbHViX3RyYWluaW5nKSAlPiUKICBzdGVwX211dGF0ZV9hdChhbGxfbnVtZXJpYygpLCBmbiA9IH4gYXMubnVtZXJpYyguKSkgJT4lICNtYWtlIGFsbCBudW1lcmljIHZhcnMgbnVtZXJpYwogIHN0ZXBfbXV0YXRlKHN1Yl9ncmFkZSA9IGZjdF9jb2xsYXBzZShzdWJfZ3JhZGUsIEEgPSBjKCJBMSIsIkEyIiwiQTMiLCJBNCIsIkE1IiksIAogICAgICAgICAgICAgICBCID0gYygiQjEiLCJCMiIsIkIzIiwiQjQiLCJCNSIpLCAKICAgICAgICAgICAgICAgQyA9IGMoIkMxIiwiQzIiLCJDMyIsIkM0IiwiQzUiKSwgI2NvbGxhcHNpbmcgbGV2ZWxzIGluIHN1Yl9ncmFkZSBpbnRvIGxlc3MKICAgICAgICAgICAgICAgRCA9IGMoIkQxIiwiRDIiLCJEMyIsIkQ0IiwiRDUiKSwgCiAgICAgICAgICAgICAgIEUgPSBjKCJFMSIsIkUyIiwiRTMiLCJFNCIsIkU1IiksCiAgICAgICAgICAgICAgIGYgPSBjKCJGMSIsIkYyIiwiRjMiLCJGNCIsIkY1IiksCiAgICAgICAgICAgICAgIEcgPSBjKCJHMSIsIkcyIiwiRzMiLCJHNCIsIkc1IikpKSAlPiUKICBzdGVwX25vcm1hbGl6ZShhbGxfcHJlZGljdG9ycygpLC1hbGxfbm9taW5hbCgpKSAlPiUgI25vcm1hbGl6ZSBxdWFudGl0YXRpdmUgdmFycwogIHN0ZXBfZHVtbXkoYWxsX25vbWluYWwoKSwtYWxsX291dGNvbWVzKCkpICNtYWtlIGNhdGVnb3JpY2FsIHZhcnMgZHVtbXkgdmFycwoKCiN1c2luZyBwcmVwIGFuZCBqdWljZSB0byBtYWtlIHN1cmUgZXZlcnl0aGluZyB3ZW50IGFzIHBsYW5uZWQKbGVuZGluZ19yZWNpcGUgJT4lCiAgcHJlcChsZW5kaW5nX2NsdWJfdHJhaW5pbmcpICU+JQogIGp1aWNlKCkKYGBgCgoKNS4gU2V0IHVwIHRoZSBsYXNzbyBtb2RlbCBhbmQgd29ya2Zsb3cuIFdlIHdpbGwgdHVuZSB0aGUgYHBlbmFsdHlgIHBhcmFtZXRlci4KYGBge3J9CiNsYXNzbyBtb2RlbApsZW5kaW5nX2xhc3NvX21vZCA8LQogIGxvZ2lzdGljX3JlZyhtaXh0dXJlID0gMSkgJT4lCiAgc2V0X2VuZ2luZSgiZ2xtbmV0IikgJT4lCiAgc2V0X2FyZ3MocGVuYWx0eSA9IHR1bmUoKSkgJT4lCiAgc2V0X21vZGUoImNsYXNzaWZpY2F0aW9uIikKCiNsYXNzbyB3b3JrZmxvdwpsZW5kaW5nX2xhc3NvX3dmIDwtCiAgd29ya2Zsb3coKSAlPiUKICBhZGRfcmVjaXBlKGxlbmRpbmdfcmVjaXBlKSAlPiUKICBhZGRfbW9kZWwobGVuZGluZ19sYXNzb19tb2QpCgpsZW5kaW5nX2xhc3NvX3dmCmBgYAoKCjYuIFNldCB1cCB0aGUgbW9kZWwgdHVuaW5nIGZvciB0aGUgYHBlbmFsdHlgIHBhcmFtZXRlci4gQmUgc3VyZSB0byBhZGQgdGhlIGBjb250cm9sX3N0YWNrX2dyaWQoKWAgZm9yIHRoZSBgY29udHJvbGAgYXJndW1lbnQgc28gd2UgY2FuIHVzZSB0aGVzZSByZXN1bHRzIGxhdGVyIHdoZW4gd2Ugc3RhY2suIEZpbmQgdGhlIGFjY3VyYWN5IGFuZCBhcmVhIHVuZGVyIHRoZSByb2MgY3VydmUgZm9yIHRoZSBtb2RlbCB3aXRoIHRoZSBiZXN0IHR1bmluZyBwYXJhbWV0ZXIuICBVc2UgNS1mb2xkIGN2LgoKYGBge3J9CnNldC5zZWVkKDQ5NCkgI2ZvciByZXByb2R1Y2libGUgNS1mb2xkCgojNS1mb2xkIGNyb3NzLXZhbGlkYXRpb24KbGVuZGluZ19jdiA8LSB2Zm9sZF9jdihsZW5kaW5nX2NsdWJfdHJhaW5pbmcsIHYgPSA1KQoKI3BlbmFsdHkgZ3JpZDogMTAgbGV2ZWxzCnBlbmFsdHlfZ3JpZCA8LSBncmlkX3JlZ3VsYXIocGVuYWx0eSgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IDEwKQoKI21ha2Ugc3VyZSBwcmVkaWN0aW9ucyBhbmQgd29ya2Zsb3dzIGFyZSBzYXZlZCBmb3Igc3RhY2tpbmcgaW4gdGhlIGZ1dHVyZQpjdHJsX2dyaWQgPC0gY29udHJvbF9zdGFja19ncmlkKCkKCiN0dW5lIHVzaW5nIDUtZm9sZCBjdgpsZW5kaW5nX2xhc3NvX3R1bmUgPC0KICBsZW5kaW5nX2xhc3NvX3dmICU+JQogIHR1bmVfZ3JpZCgKICAgIHJlc2FtcGxlcyA9IGxlbmRpbmdfY3YsCiAgICBncmlkID0gcGVuYWx0eV9ncmlkLAogICAgY29udHJvbCA9IGN0cmxfZ3JpZAogICkKCmxlbmRpbmdfYWNjdXJhY3kgPC0gbGVuZGluZ19sYXNzb190dW5lICU+JQogIGNvbGxlY3RfbWV0cmljcygpICU+JQogIGZpbHRlcigubWV0cmljID09ICJhY2N1cmFjeSIpCgpsZW5kaW5nX3JvY19hdWMgPC0gbGVuZGluZ19sYXNzb190dW5lICU+JQogIGNvbGxlY3RfbWV0cmljcygpICU+JQogIGZpbHRlcigubWV0cmljID09ICJyb2NfYXVjIikKCmdncGxvdChsZW5kaW5nX2FjY3VyYWN5LCBhZXMoeCA9IHBlbmFsdHksIHkgPSBtZWFuKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfbG9nMTAoCiAgIGJyZWFrcyA9IHNjYWxlczo6dHJhbnNfYnJlYWtzKCJsb2cxMCIsIGZ1bmN0aW9uKHgpIDEwXngpLAogICBsYWJlbHMgPSBzY2FsZXM6OnRyYW5zX2Zvcm1hdCgibG9nMTAiLHNjYWxlczo6bWF0aF9mb3JtYXQoMTBeLngpKSkgKwogIGdndGl0bGUoIkFjY3VyYWN5IG92ZXIgZGlmZmVyZW50IHBlbmFsdGllcyIpICsKICB5bGFiKCJhY2N1cmFjeSIpCgpnZ3Bsb3QobGVuZGluZ19yb2NfYXVjLCBhZXMoeCA9IHBlbmFsdHksIHkgPSBtZWFuKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX3hfbG9nMTAoCiAgIGJyZWFrcyA9IHNjYWxlczo6dHJhbnNfYnJlYWtzKCJsb2cxMCIsIGZ1bmN0aW9uKHgpIDEwXngpLAogICBsYWJlbHMgPSBzY2FsZXM6OnRyYW5zX2Zvcm1hdCgibG9nMTAiLHNjYWxlczo6bWF0aF9mb3JtYXQoMTBeLngpKSkgKwogIGdndGl0bGUoIkFyZWEgdW5kZXIgcm9jIGN1cnZlIG92ZXIgZGlmZmVyZW50IHBlbmFsdGllcyIpICsKICB5bGFiKCJyb2NfYXVjIikKCiNzZWxlY3QgYmVzdCB0dW5pbmcgcGFyYW1ldGVyIChhY2N1cmFjeSBhbmQgYXJlYSB1bmRlciByb2MgY3VydmUpCmJlc3RfcGFyYW1fbGVuZGluZyA8LSBsZW5kaW5nX2xhc3NvX3R1bmUgJT4lCiAgc2VsZWN0X2Jlc3QobWV0cmljID0gImFjY3VyYWN5IiwgInJvY19hdWMiKQoKI0lzIGl0IGFsd2F5cyB0aGUgY2FzZSB0aGF0IHRoZSBiZXN0IHR1bmluZyBwYXJhbWV0ZXIgd2lsbCBiZSB0aGUgc2FtZSBmb3IgdGhlc2UgdHdvIG1ldHJpY3M/CmJlc3RfcGFyYW1fbGVuZGluZwoKI2Jlc3QgdHVuaW5nIHBhcmFtZXRlciBpcyAwLjAwMDQ2NDE1ODkKCiNhY2N1cmFjeSBmb3IgYmVzdCB0dW5pbmcgcGFyYW1ldGVyOiAwLjc0ODcyOTYKI2FyZWEgdW5kZXIgcm9jIGN1cnZlIGZvciBiZXN0IHR1bmluZyBwYXJhbWV0ZXI6IDAuNzY1NjY1MQpgYGAKCmBgYHtyfQojIGZpbmFsaXplIHdvcmtmbG93CmxlbmRpbmdfbGFzc29fZmluYWxfd2YgPC0gbGVuZGluZ19sYXNzb193ZiAlPiUKICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X3BhcmFtX2xlbmRpbmcpCgojZml0IGZpbmFsIG1vZGVsIHdpdGggYmVzdCB0dW5pbmcgcGFyYW1ldGVyCmxlbmRpbmdfbGFzc29fZmluYWxfbW9kIDwtIGxlbmRpbmdfbGFzc29fZmluYWxfd2YgJT4lCiAgZml0KGRhdGEgPSBsZW5kaW5nX2NsdWIpCmBgYAoKCjcuIFNldCB1cCB0aGUgcmVjaXBlIGFuZCB0aGUgcHJlLXByb2Nlc3Npbmcgc3RlcHMgdG8gYnVpbGQgYSByYW5kb20gZm9yZXN0IG1vZGVsLiBZb3Ugc2hvdWxkbid0IGhhdmUgdG8gZG8gYXMgbWFueSBzdGVwcy4gVGhlIG9ubHkgc3RlcCB5b3Ugc2hvdWxkIG5lZWQgdG8gZG8gaXMgbWFraW5nIGFsbCBpbnRlZ2VycyBudW1lcmljLgpgYGB7cn0KI3ByZXByb2Nlc3NpbmcKbGVuZGluZ19yZWNpcGVfZm9yZXN0IDwtIHJlY2lwZShDbGFzcyB+IC4sIGRhdGEgPSBsZW5kaW5nX2NsdWJfdHJhaW5pbmcpICU+JQogIHN0ZXBfbXV0YXRlX2F0KGFsbF9udW1lcmljKCksIGZuID0gfiBhcy5udW1lcmljKC4pKQpgYGAKCjguIFNldCB1cCB0aGUgcmFuZG9tIGZvcmVzdCBtb2RlbCBhbmQgd29ya2Zsb3cuIFdlIHdpbGwgdHVuZSB0aGUgYG10cnlgIGFuZCBgbWluX25gIHBhcmFtZXRlcnMgYW5kIHNldCB0aGUgbnVtYmVyIG9mIHRyZWVzLCBgdHJlZXNgLCB0byAxMDAgKG90aGVyd2lzZSB0aGUgbmV4dCBzdGVwcyB0YWtlIHRvbyBsb25nKS4KYGBge3J9CiNidWlsZCBtb2RlbApsZW5kaW5nX2ZvcmVzdF9tb2QgPC0KICByYW5kX2ZvcmVzdChtdHJ5ID0gdHVuZSgpLCBtaW5fbiA9IHR1bmUoKSwgdHJlZXMgPSAyMDApICU+JQogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpICU+JQogIHNldF9lbmdpbmUoInJhbmdlciIpCgojd29ya2Zsb3cKbGVuZGluZ19mb3Jlc3Rfd2YgPC0KICB3b3JrZmxvdygpICU+JQogIGFkZF9yZWNpcGUobGVuZGluZ19yZWNpcGVfZm9yZXN0KSAlPiUKICBhZGRfbW9kZWwobGVuZGluZ19mb3Jlc3RfbW9kKQoKbGVuZGluZ19mb3Jlc3Rfd2YKYGBgCgoKOS4gU2V0IHVwIHRoZSBtb2RlbCB0dW5pbmcgZm9yIGJvdGggdGhlIGBtdHJ5YCBhbmQgYG1pbl9uYCBwYXJhbWV0ZXJzLiBCZSBzdXJlIHRvIGFkZCB0aGUgYGNvbnRyb2xfc3RhY2tfZ3JpZCgpYCBmb3IgdGhlIGBjb250cm9sYCBhcmd1bWVudCBzbyB3ZSBjYW4gdXNlIHRoZXNlIHJlc3VsdHMgbGF0ZXIgd2hlbiB3ZSBzdGFjay4gVXNlIG9ubHkgMyBsZXZlbHMgaW4gdGhlIGdyaWQuIEZvciB0aGUgYG10cnlgIHBhcmFtZXRlciwgeW91IG5lZWQgdG8gcHV0IGBmaW5hbGl6ZShtdHJ5KCksIGxlbmRpbmdfdHJhaW5pbmcgJT4lIHNlbGVjdCgtQ2xhc3MpKWAgaW4gYXMgYW4gYXJndW1lbnQgaW5zdGVhZCBvZiBqdXN0IGBtdHJ5KClgLCB3aGVyZSBgbGVuZGluZ190cmFpbmluZ2AgaXMgdGhlIG5hbWUgb2YgeW91ciB0cmFpbmluZyBkYXRhLiBUaGlzIGlzIGJlY2F1c2UgdGhlIGBtdHJ5KClgIGdyaWQgd2lsbCBvdGhlcndpc2UgaGF2ZSB1bmtub3ducyBpbiBpdC4gVGhpcyBwYXJ0IGNhbiB0YWtlIGEgd2hpbGUgdG8gcnVuLgpgYGB7cn0Kc2V0LnNlZWQoNDk0KQoKbWV0cmljIDwtIG1ldHJpY19zZXQocm9jX2F1YywgYWNjdXJhY3kpCgojY2hvb3NlIHBlbmFsdHkgcGFyYW1ldGVyIHZhbHVlcwpwZW5hbHR5X2dyaWRfZm9yZXN0IDwtIGdyaWRfcmVndWxhcihmaW5hbGl6ZShtdHJ5KCksIGxlbmRpbmdfY2x1Yl90cmFpbmluZyAlPiUgc2VsZWN0KC1DbGFzcykpLCBtaW5fbigpLCBsZXZlbHMgPSAzKQoKcGVuYWx0eV9ncmlkX2ZvcmVzdAoKY3RybF9ncmlkIDwtIGNvbnRyb2xfc3RhY2tfZ3JpZCgpCgojdHVuZSB1c2luZyA1LWZvbGQgY3YKbGVuZGluZ19mb3Jlc3RfdHVuZSA8LQogIGxlbmRpbmdfZm9yZXN0X3dmICU+JQogIHR1bmVfZ3JpZCgKICAgIHJlc2FtcGxlcyA9IGxlbmRpbmdfY3YsCiAgICBncmlkID0gcGVuYWx0eV9ncmlkX2ZvcmVzdCwKICAgIGNvbnRyb2wgPSBjdHJsX2dyaWQsCiAgICBtZXRyaWNzID0gbWV0cmljCiAgKQoKbGVuZGluZ19mb3Jlc3RfdHVuZQpgYGAKCgoxMC4gRmluZCB0aGUgYmVzdCB0dW5pbmcgcGFyYW1ldGVycy4gV2hhdCBhcmUgdGhlIGFjY3VyYWN5IGFuZCBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmUgZm9yIHRoZSBtb2RlbCB3aXRoIHRob3NlIHR1bmluZyBwYXJhbWV0ZXJzPwpgYGB7cn0KI3NlbGVjdCBiZXN0IHR1bmluZyBwYXJhbWV0ZXIgKGFjY3VyYWN5LXdpc2UpCmJlc3RfYWNjdXJhY3lfbGVuZGluZyA8LSBsZW5kaW5nX2ZvcmVzdF90dW5lICU+JQogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpCgpiZXN0X2FjY3VyYWN5X2xlbmRpbmcKCmJlc3Rfcm9jX2xlbmRpbmcgPC0gbGVuZGluZ19mb3Jlc3RfdHVuZSAlPiUKICBzZWxlY3RfYmVzdChtZXRyaWMgPSAicm9jX2F1YyIpCgpiZXN0X3JvY19sZW5kaW5nCgpsZW5kaW5nX2ZvcmVzdF9hY2N1cmFjeSA8LSBsZW5kaW5nX2ZvcmVzdF90dW5lICU+JQogIGNvbGxlY3RfbWV0cmljcygpICU+JQogIGZpbHRlcigubWV0cmljID09ICJhY2N1cmFjeSIpCgpsZW5kaW5nX2ZvcmVzdF9yb2MgPC0gbGVuZGluZ19mb3Jlc3RfdHVuZSAlPiUKICBjb2xsZWN0X21ldHJpY3MoKSAlPiUKICBmaWx0ZXIoLm1ldHJpYyA9PSAicm9jX2F1YyIpCgojYmVzdCBtdHJ5IGlzIDEwIGFuZCBiZXN0IG1pbl9uIGlzIDIKCiNhY2N1cmFjeSBmb3IgYmVzdCB0dW5pbmcgcGFyYW1ldGVyczogMC45OTA1NjM5Cgojcm9jX2F1YyBmb3IgYmVzdCB0dW5pbmcgcGFyYW1ldGVyczogMC45OTQ5ODI4CmBgYAoKYGBge3J9CiMgZmluYWxpemUgd29ya2Zsb3cKbGVuZGluZ19mb3Jlc3RfZmluYWxfd2YgPC0gbGVuZGluZ19mb3Jlc3Rfd2YgJT4lCiAgZmluYWxpemVfd29ya2Zsb3coYmVzdF9hY2N1cmFjeV9sZW5kaW5nKQoKI2ZpdCBmaW5hbCBtb2RlbCB3aXRoIGJlc3QgdHVuaW5nIHBhcmFtZXRlcgpsZW5kaW5nX2ZvcmVzdF9maW5hbF9tb2QgPC0gbGVuZGluZ19mb3Jlc3RfZmluYWxfd2YgJT4lCiAgZml0KGRhdGEgPSBsZW5kaW5nX2NsdWJfdHJhaW5pbmcpCmBgYAoKCgoxMS4gVXNlIGZ1bmN0aW9ucyBmcm9tIHRoZSBgREFMRVhgIGFuZCBgREFMRVh0cmFgIGxpYnJhcmllcyB0byBjcmVhdGUgYSBoaXN0b2dyYW0gYW5kIGJveHBsb3Qgb2YgdGhlIHJlc2lkdWFscyBmcm9tIHRoZSB0cmFpbmluZyBkYXRhLiBIb3cgZG8gdGhleSBsb29rPyBBbnkgaW50ZXJlc3RpbmcgYmVoYXZpb3I/CmBgYHtyfQojY3JlYXRlIGV4cGxhaW5lciAoImFuIG9iamVjdCB0aGF0IHByb3ZpZGVzIGEgdW5pZm9ybSBpbnRlcmZhY2UgZm9yIGRpZmZlcmVudCBtb2RlbHMuIikKbGVuZGluZ19mb3Jlc3RfZXhwbGFpbiA8LSAKICBleHBsYWluX3RpZHltb2RlbHMoCiAgICBtb2RlbCA9IGxlbmRpbmdfZm9yZXN0X2ZpbmFsX21vZCwKICAgIGRhdGEgPSBsZW5kaW5nX2NsdWJfdHJhaW5pbmcgJT4lIAogICAgICBzZWxlY3QoLUNsYXNzKSwgCiAgICB5ID0gbGVuZGluZ19jbHViX3RyYWluaW5nICU+JQogICAgICBtdXRhdGUoQ2xhc3NfbnVtID0gYXMuaW50ZWdlcihDbGFzcyA9PSJnb29kIikpICU+JQogICAgICBwdWxsKENsYXNzX251bSksCiAgICBsYWJlbCA9ICJyZiIKICApCgoKI2NyZWF0ZSBleHBsYWluZXIgKCJhbiBvYmplY3QgdGhhdCBwcm92aWRlcyBhIHVuaWZvcm0gaW50ZXJmYWNlIGZvciBkaWZmZXJlbnQgbW9kZWxzLiIpCmxlbmRpbmdfbGFzc29fZXhwbGFpbiA8LSAKICBleHBsYWluX3RpZHltb2RlbHMoCiAgICBtb2RlbCA9IGxlbmRpbmdfbGFzc29fZmluYWxfbW9kLAogICAgZGF0YSA9IGxlbmRpbmdfY2x1Yl90cmFpbmluZyAlPiUKICAgICAgc2VsZWN0KC1DbGFzcyksIAogICAgeSA9IGxlbmRpbmdfY2x1Yl90cmFpbmluZyAlPiUKICAgICAgbXV0YXRlKENsYXNzX251bSA9IGFzLmludGVnZXIoQ2xhc3MgPT0iZ29vZCIpKSAlPiUKICAgICAgcHVsbChDbGFzc19udW0pLAogICAgbGFiZWwgPSAibGFzc28iCiAgKQoKbGFzc29fbW9kX3BlcmYgPC0gbW9kZWxfcGVyZm9ybWFuY2UobGVuZGluZ19sYXNzb19leHBsYWluKQpyZl9tb2RfcGVyZiA8LSBtb2RlbF9wZXJmb3JtYW5jZShsZW5kaW5nX2ZvcmVzdF9leHBsYWluKQoKI2hpc3RvZ3JhbSBvZiByZXNpZHVhbHMgb2YgdHJhaW5pbmcgZGF0YQpoaXN0X3Bsb3QgPC0gCiAgcGxvdChyZl9tb2RfcGVyZiwgCiAgICAgICBnZW9tID0gImhpc3RvZ3JhbSIpCgpib3hfcGxvdCA8LQogIHBsb3QocmZfbW9kX3BlcmYsIAogICAgICAgZ2VvbSA9ICJib3hwbG90IikKCmhpc3RfcGxvdCArIGJveF9wbG90CmBgYAoKKipBbHRob3VnaCB0aGlzIG1hdHRlcnMgbW9yZSBmb3IgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyB3YW50IHRoZSBwbG90IG9mIHRoZSByZXNpZHVhbHMgdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuICBJbiB0aGlzIGNhc2UsIHRoZSByZXNpZHVhbHMgc2VlbSB0byBoYXZlIGEgZ2lhbnQgc3Bpa2UgYXQgMCAod2hpY2ggaXMgZ29vZCkgYW5kIGFyZSBzbGlnaHRseSByaWdodC1za2V3ZWQuKioKCgoxMi4gVXNlIGBEQUxFWGAgZnVuY3Rpb25zIHRvIGNyZWF0ZSBhIHZhcmlhYmxlIGltcG9ydGFuY2UgcGxvdCBmcm9tIHRoaXMgbW9kZWwuIFdoYXQgYXJlIHRoZSBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXM/CmBgYHtyfQpzZXQuc2VlZCgxMCkgI3NpbmNlIHdlIGFyZSBzYW1wbGluZyAmIHBlcm11dGluZywgd2Ugc2V0IGEgc2VlZCBzbyB3ZSBjYW4gcmVwbGljYXRlIHRoZSByZXN1bHRzCnJmX3Zhcl9pbXAgPC0gCiAgbW9kZWxfcGFydHMoCiAgICBsZW5kaW5nX2ZvcmVzdF9leHBsYWluCiAgICApCgpwbG90KHJmX3Zhcl9pbXAsIHNob3dfYm94cGxvdHMgPSBUUlVFKQoKI0hlcmUsIHRoZSBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMgaW4gcHJlZGljdGluZyBDbGFzcyBhcmUgYW5udWFsX2luYyAoc2VsZi1yZXBvcnRlZCBhbm51YWwgaW5jb21lKSwgaW50X3JhdGUgKGludGVyZXN0IHJhdGUgb24gdGhlIGxvYW4pLCBhbmQgcmV2b2xfdXRpbCAoYW1vdW50IG9mIGNyZWRpdCBib3Jyb3dlciBpcyB1c2luZyByZWxhdGl2ZSB0byBhbGwgYXZhaWxhYmxlIHJldm9sdmluZyBjcmVkaXQpCmBgYAoKCjEzLiBXcml0ZSBhIGZ1bmN0aW9uIGNhbGxlZCBgY3BfcHJvZmlsZWAgdG8gbWFrZSBhIENQIHByb2ZpbGUuIFRoZSBmdW5jdGlvbiB3aWxsIHRha2UgYW4gZXhwbGFpbmVyLCBhIG5ldyBvYnNlcnZhdGlvbiwgYW5kIGEgdmFyaWFibGUgbmFtZSBhcyBpdHMgYXJndW1lbnRzIGFuZCBjcmVhdGUgYSBDUCBwcm9maWxlIGZvciBhIHF1YW50aXRhdGl2ZSBwcmVkaWN0b3IgdmFyaWFibGUuIFlvdSB3aWxsIG5lZWQgdG8gdXNlIHRoZSBgcHJlZGljdF9wcm9maWxlKClgIGZ1bmN0aW9uIGluc2lkZSB0aGUgZnVuY3Rpb24geW91IGNyZWF0ZSAtIHB1dCB0aGUgdmFyaWFibGUgbmFtZSB0aGVyZSBzbyB0aGUgcGxvdHRpbmcgcGFydCBpcyBlYXNpZXIuIFlvdSdsbCBhbHNvIHdhbnQgdG8gdXNlIGBhZXNfc3RyaW5nKClgIHJhdGhlciB0aGFuIGBhZXMoKWAgYW5kIHF1b3RlIHRoZSB2YXJpYWJsZXMuIFVzZSB0aGUgYGNwX3Byb2ZpbGUoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIG9uZSBDUCBwcm9maWxlIG9mIHlvdXIgY2hvb3NpbmcuIEJlIHN1cmUgdG8gY2hvb3NlIGEgdmFyaWFibGUgdGhhdCBpcyBudW1lcmljLCBub3QgaW50ZWdlci4gVGhlcmUgc2VlbSB0byBiZSBpc3N1ZXMgd2l0aCB0aG9zZSB0aGF0IEknbSBsb29raW5nIGludG8uCmBgYHtyfQojY2V0ZXJpcyBwYXJhYnVzIHByb2ZpbGVzLCBvciBDUCBwcm9maWxlcywgc2hvdyBob3cgb25lIHZhcmlhYmxlIGFmZmVjdHMgdGhlIG91dGNvbWUsIGhvbGRpbmcgYWxsIG90aGVyIHZhcmlhYmxlcyBmaXhlZCwgZm9yIG9uZSBvYnNlcnZhdGlvbi4KCiN2YXJfbmFtZSBtdXN0IGJlIG51bWVyaWMgKG5vdCBpbnRlZ2VyKSBhbmQgaW4gcXVvdGF0aW9ucwpjcF9wcm9maWxlIDwtIGZ1bmN0aW9uKGV4cGxhaW5lciwgbmV3X29icywgdmFyX25hbWUpIHsKICBjcHAgPC0gcHJlZGljdF9wcm9maWxlKGV4cGxhaW5lciA9IGV4cGxhaW5lciwgCiAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG5ld19vYnMsCiAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9IHZhcl9uYW1lKQogIGNwcCAlPiUgcmVuYW1lKHloYXQgPSBgX3loYXRfYCkgJT4lIAogICBnZ3Bsb3QoYWVzX3N0cmluZyh4ID0gdmFyX25hbWUsCiAgICAgICAgICAgICAgeSA9ICJ5aGF0IikpICsKICAgZ2VvbV9saW5lKCkgCn0KCm9iczQgPC0gbGVuZGluZ19jbHViX3RyYWluaW5nICU+JSAKICBzbGljZSg0KQpvYnM0CgpjcF9wcm9maWxlKGxlbmRpbmdfZm9yZXN0X2V4cGxhaW4sIG9iczQsICJpbnRfcmF0ZSIpCmBgYAoKCkZvciBhbiBleHRyYSBjaGFsbGVuZ2UsIHdyaXRlIGEgZnVuY3Rpb24gdGhhdCB3aWxsIHdvcmsgZm9yIGVpdGhlciBhIHF1YW50aXRhdGl2ZSBvciBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gCgpJZiB5b3UgbmVlZCBoZWxwIHdpdGggZnVuY3Rpb24gd3JpdGluZyBjaGVjayBvdXQgdGhlIFtGdW5jdGlvbnNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZnVuY3Rpb25zLmh0bWwpIGNoYXB0ZXIgb2YgUjREUyBieSBXaWNraGFtIGFuZCBHcm9sZW11bmQuCgoKMTQuIFVzZSBgREFMRVhgIGZ1bmN0aW9ucyB0byBjcmVhdGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3RzICh3aXRoIHRoZSBDUCBwcm9maWxlcyBpbiBncmF5KSBmb3IgdGhlIDMtNCBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMuIElmIHRoZSBpbXBvcnRhbnQgdmFyaWFibGVzIGFyZSBjYXRlZ29yaWNhbCwgeW91IGNhbiBpbnN0ZWFkIG1ha2UgYSBDUCBwcm9maWxlIGZvciAzIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YXNldCBhbmQgZGlzY3VzcyBob3cgeW91IGNvdWxkIGdvIGFib3V0IGNvbnN0cnVjdGluZyBhIHBhcnRpYWwgZGVwZW5kZW5jZSBwbG90IGZvciBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlICh5b3UgZG9uJ3QgaGF2ZSB0byBjb2RlIGl0LCBidXQgeW91IGNhbiBpZiB5b3Ugd2FudCBhbiBleHRyYSBjaGFsbGVuZ2UpLiBJZiBpdCBldmVyIGdpdmVzIHlvdSBhbiBlcnJvciB0aGF0IHNheXMsICJFcnJvcjogQ2FuJ3QgY29udmVydCBmcm9tIGBWQVJJQUJMRWAgPGRvdWJsZT4gdG8gYFZBUklBQkxFYCA8aW50ZWdlcj4gZHVlIHRvIGxvc3Mgb2YgcHJlY2lzaW9uIiwgdGhlbiByZW1vdmUgdGhhdCB2YXJpYWJsZSBmcm9tIHRoZSBsaXN0LiBJIHNlZW0gdG8gaGF2ZSBmaWd1cmVkIG91dCB3aHkgaXQncyBkb2luZyB0aGF0LCBidXQgSSBkb24ndCBrbm93IGhvdyB0byBmaXggaXQgeWV0LgoKYGBge3J9CnNldC5zZWVkKDQ5NCkgIyBzaW5jZSB3ZSB0YWtlIGEgc2FtcGxlIG9mIDEwMCBvYnMKIyBUaGlzIHRha2VzIGEgd2hpbGUgdG8gcnVuLiAKIyBJZiB3ZSBvbmx5IHdhbnQgdG8gZXhhbWluZSBhIGZldyB2YXJpYWJsZXMsIGFkZCB2YXJpYWJsZXMgYXJndW1lbnQgdG8gbW9kZWxfcHJvZmlsZS4KCnJmX3BkcCA8LSBtb2RlbF9wcm9maWxlKGV4cGxhaW5lciA9IGxlbmRpbmdfZm9yZXN0X2V4cGxhaW4sIHZhcmlhYmxlcyA9IGMoImludF9yYXRlIiwgImFubnVhbF9pbmMiLCAicmV2b2xfdXRpbCIpKQoKcGxvdChyZl9wZHAsIAogICAgIHZhcmlhYmxlcyA9IGMoImludF9yYXRlIiwgImFubnVhbF9pbmMiLCAicmV2b2xfdXRpbCIpLAogICAgIGdlb20gPSAicHJvZmlsZXMiKQpgYGAKCgoxNS4gRml0IG9uZSBtb3JlIG1vZGVsIHR5cGUgb2YgeW91ciBjaG9vc2luZyB0aGF0IHdpbGwgZmVlZCBpbnRvIHRoZSBzdGFja2luZyBtb2RlbC4KCkstbmVhcmVzdCBuZWlnaGJvcnMKYGBge3J9CiMgY3JlYXRlIGEgbW9kZWwgZGVmaW5pdGlvbgpsZW5kaW5nX2tubl9tb2QgPC0KICBuZWFyZXN0X25laWdoYm9yKAogICAgbmVpZ2hib3JzID0gdHVuZSgiayIpCiAgKSAlPiUKICBzZXRfZW5naW5lKCJra25uIikgJT4lIAogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpCgojIGNyZWF0ZSB0aGUgd29ya2Zsb3cKa25uX3dmIDwtIAogIHdvcmtmbG93KCkgJT4lIAogIGFkZF9tb2RlbChsZW5kaW5nX2tubl9tb2QpICU+JQogIGFkZF9yZWNpcGUobGVuZGluZ19yZWNpcGUpCgojIHR1bmUgaXQgdXNpbmcgNCB0dW5pbmcgcGFyYW1ldGVycwpsZW5kaW5nX2tubl90dW5lIDwtIAogIGtubl93ZiAlPiUgCiAgdHVuZV9ncmlkKAogICAgbGVuZGluZ19jdiwKICAgIG1ldHJpY3MgPSBtZXRyaWMsCiAgICBncmlkID0gNCwKICAgIGNvbnRyb2wgPSBjdHJsX2dyaWQKICApCmBgYAoKCjE2LiBDcmVhdGUgYSBtb2RlbCBzdGFjayB3aXRoIHRoZSBjYW5kaWRhdGUgbW9kZWxzIGZyb20gdGhlIHByZXZpb3VzIHBhcnRzIG9mIHRoZSBleGVyY2lzZSBhbmQgdXNlIHRoZSBgYmxlbmRfcHJlZGljdGlvbnMoKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBzdGFja2VkIG1vZGVsLiBDcmVhdGUgYSBwbG90IGV4YW1pbmluZyB0aGUgcGVyZm9ybWFuY2UgbWV0cmljcyBmb3IgdGhlIGRpZmZlcmVudCBwZW5hbHR5IHBhcmFtZXRlcnMgdG8gYXNzdXJlIHlvdSBoYXZlIGNhcHR1cmVkIHRoZSBiZXN0IG9uZS4gSWYgbm90LCBhZGp1c3QgdGhlIHBlbmFsdHkuIChISU5UOiB1c2UgdGhlIGBhdXRvcGxvdCgpYCBmdW5jdGlvbikuIFdoaWNoIG1vZGVscyBhcmUgY29udHJpYnV0aW5nIG1vc3Q/CmBgYHtyfQpsZW5kaW5nX3N0YWNrIDwtCiAgc3RhY2tzKCkgJT4lCiAgYWRkX2NhbmRpZGF0ZXMobGVuZGluZ19mb3Jlc3RfdHVuZSkgJT4lCiAgYWRkX2NhbmRpZGF0ZXMobGVuZGluZ19sYXNzb190dW5lKSAlPiUKICBhZGRfY2FuZGlkYXRlcyhsZW5kaW5nX2tubl90dW5lKQoKYXNfdGliYmxlKGxlbmRpbmdfc3RhY2spCgpsZW5kaW5nX2JsZW5kIDwtIAogIGxlbmRpbmdfc3RhY2sgJT4lIAogIGJsZW5kX3ByZWRpY3Rpb25zKCkKCmxlbmRpbmdfYmxlbmQKCmF1dG9wbG90KGxlbmRpbmdfYmxlbmQpCmBgYAoKKipUaGUgcmFuZG9tIGZvcmVzdCBtb2RlbCBpcyBjb250cmlidXRpbmcgdGhlIG1vc3QgKGluIGZhY3QsIGl0J3MgdGhlIG9ubHkgb25lIGNvbnRyaWJ1dGluZyBpbiB0aGlzIGNhc2U/KS4qKgoKCjE3LiBGaXQgdGhlIGZpbmFsIHN0YWNrZWQgbW9kZWwgdXNpbmcgYGZpdF9tZW1iZXJzKClgLiBBcHBseSB0aGUgbW9kZWwgdG8gdGhlIHRlc3QgZGF0YSBhbmQgcmVwb3J0IHRoZSBhY2N1cmFjeSBhbmQgYXJlYSB1bmRlciB0aGUgY3VydmUuIENyZWF0ZSBhIGdyYXBoIG9mIHRoZSBST0MgYW5kIGNvbnN0cnVjdCBhIGNvbmZ1c2lvbiBtYXRyaXguIENvbW1lbnQgb24gd2hhdCB5b3Ugc2VlLiBTYXZlIHRoaXMgZmluYWwgbW9kZWwgdXNpbmcgdGhlIGBzYXZlUkRTKClgIGZ1bmN0aW9uIC0gc2VlIHRoZSBbVXNlIHRoZSBtb2RlbF0oaHR0cHM6Ly9hZHZhbmNlZC1kcy1pbi1yLm5ldGxpZnkuYXBwL3Bvc3RzLzIwMjEtMDMtMTYtbWwtcmV2aWV3LyN1c2UtdGhlLW1vZGVsKSBzZWN0aW9uIG9mIHRoZSBgdGlkeW1vZGVsc2AgaW50cm8uIFdlIGFyZSBnb2luZyB0byB1c2UgdGhlIG1vZGVsIGluIHRoZSBuZXh0IHBhcnQuIFlvdSdsbCB3YW50IHRvIHNhdmUgaXQgaW4gdGhlIGZvbGRlciB3aGVyZSB5b3UgY3JlYXRlIHlvdXIgc2hpbnkgYXBwLgpgYGB7cn0KbGVuZGluZ19maW5hbF9zdGFjayA8LSBsZW5kaW5nX2JsZW5kICU+JSAKICBmaXRfbWVtYmVycygpCgpsZW5kaW5nX2ZpbmFsX3N0YWNrCgpmaW5hbF9zdGFja190YWJsZSA8LSBsZW5kaW5nX2ZpbmFsX3N0YWNrICU+JSAKICBwcmVkaWN0KG5ld19kYXRhID0gbGVuZGluZ19jbHViX3Rlc3RpbmcpICU+JSAKICBiaW5kX2NvbHMobGVuZGluZ19jbHViX3Rlc3RpbmcpICU+JQogIG11dGF0ZShjb3JyZWN0X3ByZWRpY3Rpb24gPSBpZmVsc2UoLnByZWRfY2xhc3MgPT0gQ2xhc3MsIFRSVUUsIEZBTFNFKSkKCiNhY2N1cmFjeQpmaW5hbF9zdGFja19hY2N1cmFjeSA8LSBhY2N1cmFjeShmaW5hbF9zdGFja190YWJsZSwgQ2xhc3MsIC5wcmVkX2NsYXNzKQpmaW5hbF9zdGFja19hY2N1cmFjeQojYWNjdXJhY3kgaXMgMC45OTY1Nzc1CgojYXJlYSB1bmRlciB0aGUgY3VydmUKbGVuZGluZ19maW5hbF9zdGFjayRtZXRyaWNzICU+JSAKICBmaWx0ZXIoLm1ldHJpYyA9PSAicm9jX2F1YyIpCiNyb2NfYXVjIGlzIDAuOTk0OTgyNwoKI1JPQwphdXRvcGxvdChsZW5kaW5nX2ZpbmFsX3N0YWNrKQoKI2NvbmZ1c2lvbiBtYXRyaXgKY29uZl9tYXQoZGF0YSA9IGZpbmFsX3N0YWNrX3RhYmxlLCB0cnV0aCA9IENsYXNzLCBlc3RpbWF0ZSA9IC5wcmVkX2NsYXNzKQpgYGAKCioqSSBzZWUgdGhhdCB0aGlzIGJsZW5kZWQgbW9kZWwgKHdoaWNoIGlzIGVzc2VudGlhbGx5IHRoZSByYW5kb20gZm9yZXN0IG1vZGVsIHdpdGggdGhlIGJlc3QgdHVuaW5nIHBhcmFtZXRlcnMpIGhhcyBhIHN1cGVyIGhpZ2ggYWNjdXJhY3kgYW5kIHJvY19hdWMuCgojIyBTaGlueSBhcHAKCklmIHlvdSBhcmUgbmV3IHRvIFNoaW55IGFwcHMgb3IgaXQncyBiZWVuIGF3aGlsZSBzaW5jZSB5b3UndmUgbWFkZSBvbmUsIHZpc2l0IHRoZSBTaGlueSBsaW5rcyBvbiBvdXIgY291cnNlIFtSZXNvdXJjZV0oaHR0cHM6Ly9hZHZhbmNlZC1kcy1pbi1yLm5ldGxpZnkuYXBwL3Jlc291cmNlcy5odG1sKSBwYWdlLiBJIHdvdWxkIHJlY29tbWVuZCBzdGFydGluZyB3aXRoIG15IHJlc291cmNlIGJlY2F1c2UgaXQgd2lsbCBiZSB0aGUgbW9zdCBiYXNpYy4gWW91IHdvbid0IGJlIGRvaW5nIGFueXRoaW5nIHN1cGVyIGZhbmN5IGluIHRoaXMgYXBwLiAKCkV2ZXJ5b25lIHNob3VsZCB3YXRjaCB0aGUgW1RoZW1pbmcgU2hpbnldKGh0dHBzOi8veW91dHUuYmUvYjlXV05PNFAyblkpIHRhbGsgYnkgQ2Fyc29uIFNpZXZlcnQgc28geW91IGNhbiBtYWtlIHlvdXIgYXBwIGxvb2sgYW1hemluZy4KCioqVGFza3M6KioKCllvdSBhcmUgZ29pbmcgdG8gY3JlYXRlIGFuIGFwcCB0aGF0IGFsbG93cyBhIHVzZXIgdG8gZXhwbG9yZSBob3cgdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0eSBvZiBhIGxvYW4gYmVpbmcgcGFpZCBiYWNrIChvciBtYXliZSBqdXN0IHRoZSBwcmVkaWN0ZWQgY2xhc3MgLSBlaXRoZXIgImdvb2QiIG9yICJiYWQiKSBjaGFuZ2VzIGRlcGVuZGluZyBvbiB0aGUgdmFsdWVzIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzLgoKU3BlY2lmaWNhbGx5LCB5b3Ugd2lsbCBkbyB0aGUgZm9sbG93aW5nOgoKKiBTZXQgdXAgYSBzZXBhcmF0ZSBwcm9qZWN0IGFuZCBHaXRIdWIgcmVwbyBmb3IgdGhpcyBhcHAuIE1ha2Ugc3VyZSB0aGUgc2F2ZWQgbW9kZWwgZnJvbSB0aGUgcHJldmlvdXMgcHJvYmxlbSBpcyBhbHNvIGluIHRoYXQgZm9sZGVyLiBUaGUgYXBwIG5lZWRzIHRvIGJlIGNyZWF0ZWQgaW4gYSBmaWxlIGNhbGxlZCAqZXhhY3RseSogYXBwLlIgdGhhdCBpcyBhbHNvIGluIHRoZSBwcm9qZWN0IGZvbGRlci4gICAKKiBBdCB0aGUgdG9wIG9mIHRoZSBmaWxlLCBsb2FkIGFueSBsaWJyYXJpZXMgeW91IHVzZSBpbiB0aGUgYXBwLiAgCiogVXNlIHRoZSBgcmVhZFJEUygpYCBmdW5jdGlvbiB0byBsb2FkIHRoZSBtb2RlbC4gIAoqIFlvdSBtYXkgd2FudCB0byBsb2FkIHNvbWUgb2YgdGhlIGRhdGEgdG8gdXNlCiogQ3JlYXRlIGEgdXNlciBpbnRlcmZhY2UgKHVzaW5nIHRoZSB2YXJpb3VzIGAqSW5wdXQoKWAgZnVuY3Rpb25zKSB3aGVyZSBzb21lb25lIGNvdWxkIGVudGVyIHZhbHVlcyBmb3IgZWFjaCB2YXJpYWJsZSB0aGF0IGZlZWRzIGludG8gdGhlIG1vZGVsLiBZb3Ugd2lsbCB3YW50IHRvIHRoaW5rIGhhcmQgYWJvdXQgd2hpY2ggdHlwZXMgb2YgYCpJbnB1dCgpYCBmdW5jdGlvbnMgdG8gdXNlLiBUaGluayBhYm91dCBob3cgeW91IGNhbiBiZXN0IHByZXZlbnQgbWlzdGFrZXMgKGVnLiBlbnRlcmluZyBmcmVlIHRleHQgY291bGQgbGVhZCB0byBtYW55IG1pc3Rha2VzKS4gCiogQW5vdGhlciBwYXJ0IG9mIHRoZSB1c2VyIGludGVyZmFjZSB3aWxsIGFsbG93IHRoZW0gdG8gY2hvb3NlIGEgdmFyaWFibGUgKHlvdSBjYW4gbGltaXQgdGhpcyB0byBvbmx5IHRoZSBxdWFudGl0YXRpdmUgdmFyaWFibGVzKSB3aGVyZSB0aGV5IGNhbiBleHBsb3JlIHRoZSBlZmZlY3RzIG9mIGNoYW5naW5nIHRoYXQgdmFyaWFibGUsIGhvbGRpbmcgYWxsIG90aGVycyBjb25zdGFudC4gIAoqIEFmdGVyIHRoZSB1c2VyIGhhcyBlbnRlcmVkIGFsbCB0aGUgcmVxdWlyZWQgdmFsdWVzLCB0aGUgb3V0cHV0IHdpbGwgYmUgYSBDUCBwcm9maWxlIHdpdGggdGhlIHRoZSBwcmVkaWN0ZWQgdmFsdWUgZm9yIHRoZSBkYXRhIHRoYXQgd2FzIGVudGVyZWQsIGluZGljYXRlZCBieSBhIHBvaW50LiBJIGRvbid0IHRoaW5rIHRoZSBmdW5jdGlvbnMgZnJvbSBgREFMRVhgIGFuZCBgREFMRVh0cmFgIHdpbGwgd29yayB3aXRoIGEgc3RhY2tlZCBtb2RlbCwgc28geW91J2xsIGxpa2VseSBoYXZlIHRvIChnZXQgdG8pIGRvIHNvbWUgb2YgeW91ciBvd24gY29kaW5nLiAKKiBVc2UgdGhlIGBic2xpYmAgdG8gdGhlbWUgeW91ciBzaGlueSBhcHAhICAKKiBQdWJsaXNoIHlvdXIgYXBwIHRvIFtzaGlueWFwcHMuaW9dKGh0dHBzOi8vd3d3LnNoaW55YXBwcy5pby8pLiBUaGVyZSBhcmUgaW5zdHJ1Y3Rpb25zIGZvciBkb2luZyB0aGF0IG9uIHRoZSB0dXRvcmlhbCBJIGxpbmtlZCB0byBhYm92ZS4gICAKKiBXcml0ZSBhIHBhcmFncmFwaCBvciB0d28gZGVzY3JpYmluZyB5b3VyIGFwcCBvbiB5b3VyIHdlYnNpdGUhIExpbmsgdG8gdGhlIGFwcCBhbmQgeW91ciBHaXRIdWIgcmVwb3NpdG9yeSBpbiB5b3VyIHBvc3QuIEluY2x1ZGUgYSBsaW5rIHRvIHlvdXIgcG9zdCBoZXJlLiAKCgojIyBDb2RlZCBCaWFzCgpXYXRjaCB0aGUgW0NvZGUgQmlhc10oaHR0cHM6Ly93d3cucGJzLm9yZy9pbmRlcGVuZGVudGxlbnMvZmlsbXMvY29kZWQtYmlhcy8pIGZpbG0gYW5kIHdyaXRlIGEgc2hvcnQgcmVmbGVjdGlvbi4gSWYgeW91IHdhbnQgc29tZSBwcm9tcHRzLCByZWZsZWN0IG9uOiBXaGF0IHBhcnQgb2YgdGhlIGZpbG0gaW1wYWN0ZWQgeW91IHRoZSBtb3N0PyBXYXMgdGhlcmUgYSBwYXJ0IHRoYXQgc3VycHJpc2VkIHlvdSBhbmQgd2h5PyBXaGF0IGVtb3Rpb25zIGRpZCB5b3UgZXhwZXJpZW5jZSB3aGlsZSB3YXRjaGluZz8KCkkgdGhpbmsgdGhhdCB0aGUgcXVvdGUgYW5kIGNvbW1vbiB0aGVtZSB0aGF0ICJ0aGUgcGFzdCBkd2VsbHMgd2l0aGluIG91ciBhbGdvcml0aG1zIiBpcyBjcnVjaWFsIHRvIGJlIGF3YXJlIG9mLiAgSG93IHRoZSBkYXRhIHdhcyBjb2xsZWN0ZWQgYW5kIHdobyBpcyByZXByZXNlbnRlZCAoYW5kIHdobyBpc24ndCkgcmVmbGVjdHMgd2hlcmUgb3VyIHNvY2lldHkgd2FzICphdCB0aGUgdGltZSBvZiBjb2xsZWN0aW9uLiogIFRoZXJlZm9yZSwgaG93IGFyZSB3ZSBnb2luZyB0byBtYWtlIHByb2dyZXNzIGluIGJlaW5nIGxlc3MgZGlzY3JpbWluYXRvcnkgYW5kIG1vcmUgaW5jbHVzaXZlIGluIHRoZSBmdXR1cmUgaWYgdGhlIGFsZ29yaXRobXMgYXJlIGJhc2luZyB0aGVpciBhc3NpZ25tZW50cyBvbiB0aGUgcGFzdD8gIEkgd29uZGVyIGlmIHRoZXJlIGlzIHNvbWUgd2F5IHRvIHNpbXVsYXRlIGZ1dHVyZSBkYXRhIG9yIGp1c3QgZG8gc29tZXRoaW5nIHRvIG1ha2UgcGFzdCBkYXRhIG1vcmUgcmVwcmVzZW50YXRpdmUgb2YgY3VycmVudCBhbmQgZnV0dXJlIG5lZWRzLgoKQ29kZWQgQmlhcyBhbHNvIGxlZCBtZSB0byB0aGluayBhIGxvdCBhYm91dCBob3cgd2UgY29tcGFyZSBvdXIgc29jaWV0eSB3aXRoIG9uZSBzdWNoIGFzIENoaW5hLiAgV2UgZmVlbCByZWxpZXZlZCB0aGF0IHdlIGRvbid0IGxpdmUgaW4gYSBzb2NpZXR5IHdpdGggY29uc3RhbnQgZmFjaWFsIHJlY29nbml0aW9uIGFuZCBzb2NpYWwgY3JlZGl0IHNjb3JlcywgYnV0IG91ciBzb2NpZXR5IGlzIHRyYWNraW5nIHVzIGFuZCBiZWluZyBleGNsdXNpb25hcnkgYmFzZWQgb24gdGhlIChvZnRlbiBpbmFjY3VyYXRlKSBkYXRhIHRoZXkgZ2F0aGVyIGFib3V0IHVzLiAgV2UgaGF2ZSBwZXJ2YXNpdmUgYWxnb3JpdGhtaWMgYmlhcyB0aGF0IGlzIGdlYXJlZCB0b3dhcmQgY2FwaXRhbGlzdGljIGdhaW4sIGtlZXBpbmcgdGhlIHJpY2ggcmljaCwgYW5kIGtlZXBpbmcgbWlub3JpdHkgcG9wdWxhdGlvbnMgZG93biAoZXguIHRoZSBmYWNpYWwgcmVjb2duaXRpb24gaW4gdGhlIGFwYXJ0bWVudCBidWlsZGluZyBmcm9tIHRoZSBkb2N1bWVudGFyeSkuICBJdCB3YXMgY3JlZXB5IChhbmQgdHJ1ZSkgdGhhdCB0aGUgZG9jdW1lbnRhcnkgc2FpZCB0aGF0IHRoZSBtYWluIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgc3VydmVpbGxhbmNlIG9mIHRoZSBVUyBhbmQgQ2hpbmEgaXMgdGhhdCBDaGluYSBpcyBiZWluZyB0cmFuc3BhcmVudCBhYm91dCBpdCwgd2hlcmVhcyB3ZSBhcmUgbGV0dGluZyBpdCBmbHkgdW5kZXIgdGhlIHJhZGFyLgoKUkVNRU1CRVIgVE8gQUREIFlPVVIgR0lUSFVCIExJTksgQVQgVEhFIFRPUCBPRiBUSEUgUEFHRSBBTkQgVU5DT01NRU5UIFRIRSBga25pdHJgIE9QVElPTlMuCgoK